﻿// Decompiled with JetBrains decompiler
// Type: Netick.InterestManagement
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Numerics;

#nullable disable
namespace Netick;

/// <summary>
/// Manages interest groups and area of interest (AoI) layers.
/// </summary>
public sealed class InterestManagement
{
  public readonly NetickEngine Engine;
  public readonly int TotalGroupCount;
  internal readonly InterestManagementGroup GlobalGroup;
  public readonly InterestManagementGroup[] AllGroups;
  public readonly InterestManagementGroup[] CustomGroups;
  public readonly AreaOfInterestLayer[] AoILayers;
  internal SimpleAllocator _allocator;
  private HashSet<RemovedEntityFromGroup> _removedEntities;

  internal unsafe InterestManagement(NetickEngine engine)
  {
    this.Engine = engine;
    this._allocator = new SimpleAllocator();
    this._removedEntities = new HashSet<RemovedEntityFromGroup>(engine._config.MaxObjects);
    Vector3 worldSize = engine._config.EnableInterestManagement ? engine._config.AoIWorldSize : Vector3.One;
    int length = engine._config.EnableInterestManagement ? engine._config.AoILayerCount : 1;
    int interestGroupCount = engine._config.EnableInterestManagement ? this.Engine._config.CustomInterestGroupCount : 0;
    this.TotalGroupCount = interestGroupCount + 1;
    for (int index = 0; index < length; ++index)
      this.TotalGroupCount += AreaOfInterestLayer.CalcAoILayerGroupCount(worldSize, engine.Config.EnableInterestManagement ? (int) engine._config.AoICellSizes[index] : 1);
    this.AllGroups = new InterestManagementGroup[this.TotalGroupCount];
    this.CustomGroups = new InterestManagementGroup[interestGroupCount];
    this.AoILayers = new AreaOfInterestLayer[length];
    int num = 0;
    this.GlobalGroup = this.CreateAoIGroup(ref num, this.Engine._config.MaxObjects, new Vector3(), new NetickVector3Int());
    for (int index = 0; index < interestGroupCount; ++index)
      this.CustomGroups[index] = this.CreateAoIGroup(ref num, 100, new Vector3(), new NetickVector3Int());
    for (int index = 0; index < length; ++index)
      this.AoILayers[index] = new AreaOfInterestLayer(this, worldSize, engine.Config.EnableInterestManagement ? (float) (int) engine._config.AoICellSizes[index] : 1f, ref num);
  }

  internal InterestManagementGroup CreateAoIGroup(
    ref int counter,
    int initialCapacity,
    Vector3 pos,
    NetickVector3Int posInt)
  {
    InterestManagementGroup aoIgroup = new InterestManagementGroup(this, counter, initialCapacity, pos, posInt);
    this.AllGroups[counter] = aoIgroup;
    ++counter;
    if (this.Engine.IsServer)
      aoIgroup.Entity.Position = pos;
    return aoIgroup;
  }

  internal void ResetForClient(int index)
  {
    List<Entity> entitiesList = this.Engine._sim.EntitiesList;
    this.Engine._internalReplicator.Entity.ResetForClient(index);
    for (int index1 = 0; index1 < entitiesList.Count; ++index1)
      entitiesList[index1].ResetForClient(index);
    for (int index2 = 0; index2 < this.AllGroups.Length; ++index2)
      this.AllGroups[index2].Entity.ResetForClient(index);
  }

  internal void ResetIM()
  {
    for (int index1 = 0; index1 < this.Engine.MaxClients; ++index1)
    {
      ServerConnection client = this.Engine.Server.Clients[index1];
      int index2 = client.Index;
      List<InterestManagementGroup> interestGroups = client._interestGroups;
      for (int index3 = 0; index3 < interestGroups.Count; ++index3)
        interestGroups[index3].Clients[index2] = false;
      interestGroups.Clear();
    }
  }

  internal void CalcAoI()
  {
    if (!this.Engine._config.EnableInterestManagement)
      return;
    for (int index1 = 0; index1 < this.Engine.MaxClients; ++index1)
    {
      ServerConnection client = this.Engine.Server.Clients[index1];
      int index2 = client.Index;
      for (int index3 = 0; index3 < client.AoIQueries.Count; ++index3)
        this.AoILayers[client.AoIQueries[index3].Layer].CalcBoxInterest(index2, client._interestGroups, client.AoIQueries[index3].Bounds);
      client.AoIQueries.Clear();
    }
  }

  internal void ProcessChanges()
  {
    this.Engine._profiler.ProcessStateChangesProfiler?.Begin();
    Entity[] entitiesWorldIndexArray = this.Engine._sim.EntitiesWorldIndexArray;
    this.Engine._internalReplicator.Entity.Process();
    for (int index = 0; index < this.AllGroups.Length; ++index)
      this.ProcessGroup(this.AllGroups[index], entitiesWorldIndexArray);
    this.Engine._profiler.ProcessStateChangesProfiler?.End();
    foreach (RemovedEntityFromGroup removedEntity in this._removedEntities)
    {
      bool removeFromList = removedEntity.Previous != removedEntity.Entity.InternalInterestGroup;
      removedEntity.Previous.RemoveEntity(removedEntity.Entity, removedEntity.PreviousGroupTableIndex, removeFromList);
    }
    this._removedEntities.Clear();
  }

  internal unsafe void ProcessGroup(InterestManagementGroup group, Entity[] entitiesWorldIndexTable)
  {
    Entity entity = group.Entity;
    NativeArray<int> idToWorldIndex = group._idToWorldIndex;
    for (long index1 = 0; index1 < (long) entity._changesLen; ++index1)
    {
      int* numPtr = (int*) ((IntPtr) entity._changes + (IntPtr) (index1 * 8L));
      if (*numPtr != -1)
      {
        int index2 = idToWorldIndex[*numPtr];
        if (index2 != -1)
          entitiesWorldIndexTable[index2]?.Process();
        else
          *numPtr = -1;
      }
    }
    for (long index3 = 0; index3 < (long) entity._changesWithClientsLen; ++index3)
    {
      int* numPtr = (int*) ((IntPtr) entity._changesWithClients + (IntPtr) (index3 * 8L));
      if (*numPtr != -1)
      {
        int index4 = idToWorldIndex[*numPtr];
        if (index4 != -1)
          entitiesWorldIndexTable[index4]?.Process();
        else
          *numPtr = -1;
      }
    }
    group.Entity.ProcessForGroup();
  }

  internal void ServerSetEntityInterestGroup(Entity entity, InterestManagementGroup newGroup)
  {
    if (this.Engine.IsClient || entity.InternalInterestGroup == newGroup)
      return;
    if (entity.InternalInterestGroup != null)
      this._removedEntities.Add(new RemovedEntityFromGroup(entity, entity.GroupTableId, entity.InternalInterestGroup));
    entity.InternalInterestGroup = newGroup;
    if (newGroup == null)
      this.Engine._entityMetaReplicator.SetInterestGroup(entity, -1);
    else
      newGroup.AddEntity(entity);
  }

  internal void ClientSetEntityInterestGroup(Entity entity, int newPosIndex)
  {
    InterestManagementGroup allGroup = newPosIndex == -1 ? (InterestManagementGroup) null : this.AllGroups[newPosIndex];
    if (allGroup == entity.InternalInterestGroup)
      return;
    entity.InternalInterestGroup?.RemoveEntity(entity, -1);
    entity.InternalInterestGroup = allGroup;
    allGroup?.AddEntity(entity);
  }

  internal bool IsWithInClientAOI(Entity entity, int groupIndex)
  {
    if (groupIndex == -1)
      return false;
    InterestManagementGroup allGroup = this.Engine._IM.AllGroups[groupIndex];
    if (entity.NarrowPhaseFiltered)
      return false;
    if (entity.UserEntity.BroadPhaseFilter == BroadPhaseFilter.Global)
      return true;
    foreach (InterestManagementGroup receivedGroup in this.Engine._cliSim.ReceivedGroups)
    {
      if (allGroup == receivedGroup)
        return true;
    }
    return false;
  }

  public InterestManagementGroup GetAoIGroupByPosition(Vector3 position, int aoiLayerIndex)
  {
    return this.AoILayers[aoiLayerIndex].GetGroupByPosition(position);
  }
}
